package com.minimalist.common.utils;

import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import org.apache.commons.collections4.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.StringUtils;

import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Map;

/**
 * PDF生成和填充工具类
 * 支持在指定坐标位置填充文本内容到PDF文件中
 *
 * @author minimalist
 * @since 2024-01-01
 */
public class PdfGenerate{
    private static final Logger log = LoggerFactory.getLogger(PdfGenerate.class);

    /**
     * 字段填充信息
     */
    public static class FieldInfo{
        private String content;  // 填充内容
        private float x;         // X坐标
        private float y;         // Y坐标
        private float fontSize;  // 字体大小
        private int page;        // 页码（从1开始）

        public FieldInfo(){
            this.fontSize = 12f;
            this.page = 1;
        }

        public FieldInfo(String content, float x, float y){
            this.content = content;
            this.x = x;
            this.y = y;
            this.fontSize = 12f;
            this.page = 1;
        }

        public FieldInfo(String content, float x, float y, float fontSize, int page){
            this.content = content;
            this.x = x;
            this.y = y;
            this.fontSize = fontSize;
            this.page = page;
        }

        public String getContent(){
            return content;
        }

        public void setContent(String content){
            this.content = content;
        }

        public float getX(){
            return x;
        }

        public void setX(float x){
            this.x = x;
        }

        public float getY(){
            return y;
        }

        public void setY(float y){
            this.y = y;
        }

        public float getFontSize(){
            return fontSize;
        }

        public void setFontSize(float fontSize){
            this.fontSize = fontSize;
        }

        public int getPage(){
            return page;
        }

        public void setPage(int page){
            this.page = page;
        }
    }

    /**
     * 在PDF指定坐标位置填充文本内容
     *
     * @param templatePath PDF模板文件路径
     * @param outputPath   输出PDF文件路径
     * @param fields       字段填充信息Map，key为字段标识，value为FieldInfo
     * @return 是否填充成功
     */
    public static boolean fillPdfWithFields(String templatePath, String outputPath, Map<String, FieldInfo> fields){
        if (!StringUtils.hasText(templatePath) || !StringUtils.hasText(outputPath) || fields == null || fields.isEmpty()){
            log.error("PDF填充参数不能为空");
            return false;
        }

        PdfReader reader = null;
        PdfStamper stamper = null;
        FileOutputStream fos = null;

        try{
            // 读取PDF模板
            reader = new PdfReader(templatePath);
            fos = new FileOutputStream(outputPath);
            stamper = new PdfStamper(reader, fos);

            // 创建中文字体 - 使用Spring Boot Resource加载字体文件
            BaseFont baseFont;
            try{
                // 使用Spring Boot ClassPathResource加载字体文件
                ClassPathResource fontResource = new ClassPathResource("songti.ttf");
                if (fontResource.exists()){
                    try (InputStream fontStream = fontResource.getInputStream()){
                        byte[] fontBytes = fontStream.readAllBytes();
                        baseFont = BaseFont.createFont("songti.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, true, fontBytes, null);
                        log.info("成功加载宋体字体文件: {}", fontResource.getPath());
                    }
                } else{
                    throw new RuntimeException("字体文件不存在: songti.ttf");
                }
            } catch (Exception e){
                log.warn("宋体字体文件不可用，尝试使用系统字体: {}", e.getMessage());
                try{
                    // 后备方案：使用系统内置的中文字体
                    baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
                } catch (Exception e2){
                    log.warn("STSong-Light字体不可用，使用默认字体: {}", e2.getMessage());
                    // 最后后备：使用内置的Helvetica字体
                    baseFont = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED);
                }
            }

            // 遍历所有字段进行填充
            for (Map.Entry<String, FieldInfo> entry : fields.entrySet()){
                String fieldKey = entry.getKey();
                FieldInfo fieldInfo = entry.getValue();

                if (fieldInfo == null || !StringUtils.hasText(fieldInfo.getContent())){
                    log.warn("字段 {} 的内容为空，跳过填充", fieldKey);
                    continue;
                }

                // 如果没有设置字体大小，使用小四号（12pt）作为默认值
                if (fieldInfo.getFontSize() <= 0){
                    fieldInfo.setFontSize(12f);
                }

                // 处理多行文本
                String processedContent = processMultiLineText(fieldInfo.getContent());
                fieldInfo.setContent(processedContent);

                // 在指定页面和坐标填充文本
                fillTextAtPosition(stamper, fieldInfo, baseFont);
                log.debug("字段 {} 填充完成: 内容={}, 坐标=({}, {}), 页码={}, 字体大小={}",
                        fieldKey, fieldInfo.getContent(), fieldInfo.getX(), fieldInfo.getY(), fieldInfo.getPage(), fieldInfo.getFontSize());
            }

            // 设置表单不可编辑
            stamper.setFormFlattening(true);

            log.info("PDF填充完成，输出文件: {}", outputPath);
            return true;

        } catch (Exception e){
            log.error("PDF填充失败: {}", e.getMessage(), e);
            return false;
        } finally{
            // 关闭资源
            try{
                if (stamper != null){
                    stamper.close();
                }
                if (reader != null){
                    reader.close();
                }
                if (fos != null){
                    fos.close();
                }
            } catch (Exception e){
                log.error("关闭PDF资源失败: {}", e.getMessage(), e);
            }
        }
    }

    /**
     * 根据PDF表单字段填充内容
     *
     * @param templatePath PDF模板文件路径
     * @param outputPath   输出PDF文件路径
     * @param formFields   表单字段Map，key为字段名，value为填充内容
     * @return 是否填充成功
     */
    public static boolean fillPdfFormFields(String templatePath, String outputPath, Map<String, String> formFields){
        if (!StringUtils.hasText(templatePath)
                || !StringUtils.hasText(outputPath)
                || formFields == null
                || formFields.isEmpty()){
            log.error("PDF表单填充参数不能为空");
            return false;
        }

        PdfReader reader = null;
        PdfStamper stamper = null;
        FileOutputStream fos = null;

        try{
            // 读取PDF模板
            reader = new PdfReader(templatePath);
            fos = new FileOutputStream(outputPath);
            stamper = new PdfStamper(reader, fos);

            // 获取PDF表单
            AcroFields acroFields = stamper.getAcroFields();
            if (acroFields == null){
                log.warn("PDF模板中没有找到表单字段");
                return false;
            }
            log.info("PDF模板表单字段: {}", acroFields.getFields().keySet());

            // 创建中文字体 - 使用Spring Boot Resource加载字体文件
            BaseFont baseFont;
            try{
                // 使用Spring Boot ClassPathResource加载字体文件
                ClassPathResource fontResource = new ClassPathResource("songti.ttf");
                if (fontResource.exists()){
                    try (InputStream fontStream = fontResource.getInputStream()){
                        byte[] fontBytes = fontStream.readAllBytes();
                        baseFont = BaseFont.createFont("songti.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, true, fontBytes, null);
                        log.info("成功加载宋体字体文件: {}", fontResource.getPath());
                    }
                } else{
                    throw new RuntimeException("字体文件不存在: songti.ttf");
                }
            } catch (Exception e){
                log.warn("宋体字体文件不可用，尝试使用系统字体: {}", e.getMessage());
                try{
                    // 后备方案：使用系统内置的中文字体
                    baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
                } catch (Exception e2){
                    log.warn("STSong-Light字体不可用，使用默认字体: {}", e2.getMessage());
                    // 最后后备：使用内置的Helvetica字体
                    baseFont = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED);
                }
            }

            // 设置字体 - 宋体小四号（12pt）
            acroFields.addSubstitutionFont(baseFont);

            // 填充表单字段
            int filledCount = 0;
            if (MapUtils.isNotEmpty(formFields)){
                for (Map.Entry<String, String> next : formFields.entrySet()){
                    String key = next.getKey();
                    String value = StringUtils.hasText(next.getValue()) ? next.getValue() : "";

                    try{
                        // 默认12号字体
                        float fontSize = 12f;
                        PdfString da = acroFields.getFieldItem(key).getMerged(0).getAsString(PdfName.DA);
                        if (da != null){
                            Object[] dab = AcroFields.splitDAelements(da.toUnicodeString());
                            if (dab[AcroFields.DA_SIZE] != null){
                                fontSize = (Float) dab[AcroFields.DA_SIZE];
                            }
                        }

                        // 文本框宽度
                        Rectangle position = acroFields.getFieldPositions(key).getFirst().position;
                        float textBoxWidth = position.getWidth();
                        // 文本框高度
                        float textBoxHeight = position.getHeight();
                        // 文本单行行高
                        float ascent = baseFont.getFontDescriptor(BaseFont.ASCENT, fontSize);
                        // baseFont渲染后的文字宽度
                        float textWidth = baseFont.getWidthPoint(value, fontSize);

                        // 文本框高度只够写一行，并且文字宽度大于文本框宽度，则缩小字体
                        if (textBoxHeight < ascent * 1.6){
                            while (textWidth > textBoxWidth && fontSize > 6){
                                fontSize--;
                                textWidth = baseFont.getWidthPoint(value, fontSize);
                            }
                        }
                        acroFields.setFieldProperty(key, "textsize", fontSize, null);
                        boolean success = acroFields.setField(key, value);
                        if (success){
                            filledCount++;
                            log.debug("字段 {} 填充成功，字体大小: {}", key, fontSize);
                        } else{
                            log.warn("字段 {} 填充失败，可能字段不存在", key);
                        }
                    } catch (Exception e){
                        log.error("填充字段 {} 时发生异常: {}", key, e.getMessage());
                    }
                }
            }

            if (filledCount == 0){
                log.warn("没有成功填充任何字段");
            }

            // 设置表单不可编辑
            stamper.setFormFlattening(true);

            log.info("PDF表单填充完成，成功填充 {} 个字段，输出文件: {}", filledCount, outputPath);
            return true;

        } catch (Exception e){
            log.error("PDF表单填充失败: {}", e.getMessage(), e);
            return false;
        } finally{
            // 关闭资源
            try{
                if (stamper != null){
                    stamper.close();
                }
                if (reader != null){
                    reader.close();
                }
                if (fos != null){
                    fos.close();
                }
            } catch (Exception e){
                log.error("关闭PDF资源失败: {}", e.getMessage(), e);
            }
        }
    }

    /**
     * 获取PDF表单中的所有字段名
     *
     * @param templatePath PDF模板文件路径
     * @return 字段名集合
     */
    public static java.util.Set<String> getPdfFormFieldNames(String templatePath){
        if (!StringUtils.hasText(templatePath)){
            log.error("PDF模板路径不能为空");
            return java.util.Collections.emptySet();
        }

        PdfReader reader = null;
        try{
            reader = new PdfReader(templatePath);
            AcroFields acroFields = reader.getAcroFields();
            if (acroFields == null){
                log.warn("PDF模板中没有找到表单字段");
                return java.util.Collections.emptySet();
            }

            java.util.Set<String> fieldNames = acroFields.getFields().keySet();
            log.info("PDF模板中找到 {} 个表单字段: {}", fieldNames.size(), fieldNames);
            return fieldNames;

        } catch (Exception e){
            log.error("获取PDF表单字段失败: {}", e.getMessage(), e);
            return java.util.Collections.emptySet();
        } finally{
            if (reader != null){
                try{
                    reader.close();
                } catch (Exception e){
                    log.error("关闭PDF资源失败: {}", e.getMessage(), e);
                }
            }
        }
    }

    /**
     * 处理多行文本，确保换行符能够正确显示
     *
     * @param text 原始文本
     * @return 处理后的文本
     */
    private static String processMultiLineText(String text){
        if (!StringUtils.hasText(text)){
            return text;
        }

        // 将不同类型的换行符统一处理
        String processedText = text.replace("\r\n", "\n")
                .replace("\r", "\n");

        // 对于PDF表单字段，保持换行符
        return processedText;
    }

    /**
     * 在指定位置填充文本（支持多行）
     *
     * @param stamper   PDF stamper
     * @param fieldInfo 字段信息
     * @param baseFont  字体
     * @throws DocumentException 文档异常
     */
    private static void fillTextAtPosition(PdfStamper stamper, FieldInfo fieldInfo, BaseFont baseFont) throws DocumentException{
        // 获取指定页面的内容字节
        PdfContentByte contentByte = stamper.getOverContent(fieldInfo.getPage());

        String content = fieldInfo.getContent();
        if (!StringUtils.hasText(content)){
            return;
        }

        // 分割多行文本
        String[] lines = content.split("\n");
        float fontSize = fieldInfo.getFontSize();
        float lineHeight = fontSize * 1.2f; // 行高为字体大小的1.2倍

        // 开始文本写入
        contentByte.beginText();
        contentByte.setFontAndSize(baseFont, fontSize);

        // 逐行渲染文本
        for (int i = 0; i < lines.length; i++){
            float yPosition = fieldInfo.getY() - (i * lineHeight);
            contentByte.setTextMatrix(fieldInfo.getX(), yPosition);
            contentByte.showText(lines[i]);
        }

        contentByte.endText();

        log.debug("多行文本填充完成，共 {} 行，行高: {}", lines.length, lineHeight);
    }
}
